package cn.iocoder.yudao.module.bpm.service.definition;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.meta.Column;
import cn.hutool.db.meta.MetaUtil;
import cn.hutool.db.meta.Table;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.constant.GlobalConstant;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateCustomFormReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.service.code.CodeService;
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
import cn.iocoder.yudao.module.bpm.service.oaapply.OaApplyService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.bpm.util.DatasourceUtil;
import cn.iocoder.yudao.module.bpm.util.JdbcToJavaUtil;
import cn.iocoder.yudao.module.bpm.util.TableUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.exception.ChainNotFoundException;
import com.yomahub.liteflow.flow.LiteflowResponse;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;

import javax.sql.DataSource;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

/**
 * 动态表单 Service 实现类
 *
 * @author 风里雾里
 */
@Service
@Validated
public class BpmFormServiceImpl implements BpmFormService {

    @Resource
    private BpmProcessInstanceService processInstanceService;

    @Resource
    OaApplyService oaApplyService;

    @Resource
    CodeService codeService;

    @Resource
    private BpmFormMapper formMapper;

    @Autowired
    FlowExecutor flowsExecutor;
    /**
     * 需要模糊查询的
     */
    private final List<String> LIKE_CLASS_NAME = Collections.singletonList("String");

    /**
     * 需要完全对比判断
     */
    private final List<String> EQ_CLASS_NAME = Arrays.asList("Integer", "Long", "Double", "Float", "Boolean", "BigDecimal");

    /**
     * 时间类型
     */
    private final List<String> TIME_CLASS_NAME = Collections.singletonList("LocalDateTime");

    @Override
    public Long createForm(BpmFormSaveReqVO createReqVO) {
        this.validateFields(createReqVO.getFields());
        Long selectCount = formMapper.selectCount("form_key", createReqVO.getFormKey());
        if (selectCount > 0) {
            throw new ServiceException(500, createReqVO.getFormKey() + "已存在");

        }
        // 插入
        BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class);
        formMapper.insert(form);
        // 返回
        return form.getId();
    }

    @Override
    public void updateForm(BpmFormSaveReqVO updateReqVO) {
        validateFields(updateReqVO.getFields());
        // 校验存在
        validateFormExists(updateReqVO.getId());
        // 更新
        BpmFormDO updateObj = BeanUtils.toBean(updateReqVO, BpmFormDO.class);
        formMapper.updateById(updateObj);
    }

    @Override
    public void deleteForm(Long id) {
        // 校验存在
        this.validateFormExists(id);
        // 删除
        formMapper.deleteById(id);
    }

    private void validateFormExists(Long id) {
        if (formMapper.selectById(id) == null) {
            throw exception(ErrorCodeConstants.FORM_NOT_EXISTS);
        }
    }

    @Override
    public BpmFormDO getForm(Long id) {
        return formMapper.selectById(id);
    }

    @Override
    public List<BpmFormDO> getFormList() {
        return formMapper.selectList();
    }

    @Override
    public List<BpmFormDO> getFormList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return formMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<BpmFormDO> getFormPage(BpmFormPageReqVO pageReqVO) {
        return formMapper.selectPage(pageReqVO);
    }

    @Override
    public Entity getById(String id, String tableName) {
        DataSource master = DatasourceUtil.getDataSource("master");
        Entity where = Entity.create(tableName);
        //获取表里所有字段
        Table tableMeta = MetaUtil.getTableMeta(master, tableName);
        Collection<Column> columns = tableMeta.getColumns();
        Optional<Column> pk = columns.stream().filter(Column::isPk).findFirst();
        if (pk.isPresent()) {
            where.set(pk.get().getName(), id);
        }
        try {
            Entity entity = Db.use().get(where);
            JsonUtils.parseJSONToArray(entity);
            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(500, "表单数据查询失败" + e.getMessage());
        }
    }

    @Override
    public Entity getByPId(String pid, String tableName) {
        DataSource master = DatasourceUtil.getDataSource("master");
        Entity where = Entity.create(tableName);
        //获取表里所有字段
        Table tableMeta = MetaUtil.getTableMeta(master, tableName);
        Collection<Column> columns = tableMeta.getColumns();
        Optional<Column> pk = columns.stream().filter(Column::isPk).findFirst();
        if (pk.isPresent()) {
            where.set("process_instance_id", pid);
        }
        try {
            Entity entity = Db.use().get(where);
            JsonUtils.parseJSONToArray(entity);
            return entity;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(500, "表单数据查询失败" + e.getMessage());
        }
    }

    @Override
    public CommonResult<String> createForm(@RequestBody JSONObject formData) {
        formData.put("user_id", getLoginUser().getId());
        formData.put("creator", getLoginUser().getId());
        formData.put("create_time", LocalDateTime.now());
        String tableName = (String) formData.get("tableName");
        String bpmDefKey = (String) formData.get("bpm_def_key");
        formData.put("eventType", "create");
        formData.put("eventTime", "before");
        if (StringUtils.isNotEmpty(bpmDefKey)) {
            formData.put("sn", codeService.getCodeByType(bpmDefKey));
        }
        LiteflowResponse liteflowResponse = flowsExecutor.execute2Resp(tableName, null, formData);
        if (!liteflowResponse.isSuccess()) {
            if (!(liteflowResponse.getCause() instanceof ChainNotFoundException)) {
                throw new ServiceException(500, liteflowResponse.getMessage());
            }
        }
        if (StringUtils.isNotEmpty(bpmDefKey)) {
            liteflowResponse = flowsExecutor.execute2Resp(bpmDefKey, null, formData);
            if (!liteflowResponse.isSuccess()) {
                if (!(liteflowResponse.getCause() instanceof ChainNotFoundException)) {
                    throw new ServiceException(500, liteflowResponse.getMessage());
                }
            }
        }
        JsonUtils.parseArrayToJSON(formData);
        DataSource master = DatasourceUtil.getDataSource("master");
        Entity entity = Entity.create(tableName);
        //获取表里所有字段
        Table tableMeta = MetaUtil.getTableMeta(master, tableName);
        Collection<Column> columns = tableMeta.getColumns();
        Optional<Column> pk = columns.stream().filter(Column::isPk).findFirst();
        String keyValue = formData.getString("id");
        if (StrUtil.isBlank(keyValue)) {
            keyValue = IdUtil.getSnowflakeNextIdStr();
        }
        Map<String, Object> toSaveFormData = handleFormDataForSave(formData, tableName, columns);
        if (pk.isPresent()) {
            formData.put(pk.get().getName(), keyValue);
            toSaveFormData.put(pk.get().getName(), keyValue);
        }
        entity.putAll(toSaveFormData);
        //审计字段
        putAuditEntityInsertData(entity, columns);
        int insert = 0;
        try {
            insert = Db.use().insert(entity);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(500, "表单数据插入失败" + e.getMessage());
        }
        formData.put("eventType", "create");
        formData.put("eventTime", "after");
        liteflowResponse = flowsExecutor.execute2Resp(tableName, null, formData);
        if (!liteflowResponse.isSuccess()) {
            if (!(liteflowResponse.getCause() instanceof ChainNotFoundException)) {
                throw new ServiceException(500, liteflowResponse.getMessage());
            }
        }
        if (StringUtils.isNotEmpty(bpmDefKey)) {
            liteflowResponse = flowsExecutor.execute2Resp(bpmDefKey, null, formData);
            if (!liteflowResponse.isSuccess()) {
                if (!(liteflowResponse.getCause() instanceof ChainNotFoundException)) {
                    throw new ServiceException(500, liteflowResponse.getMessage());
                }
            }
        }

        if (StringUtils.isNotEmpty(bpmDefKey) && "bpm_oa_apply".equals(tableName)) {
            //启动流程
            String processInstanceId = processInstanceService.createProcessInstance(getLoginUserId(),
                    new BpmProcessInstanceCreateReqDTO()
                            .setProcessDefinitionKey(bpmDefKey)
                            .setVariables(formData)
                            .setBusinessKey(String.valueOf(keyValue)));
            formData.put("process_instance_id", processInstanceId);
            updateForm(formData);
        }

        return success(keyValue);
    }

    @Override
    public CommonResult<Boolean> updateForm(JSONObject formData) {
        formData.put("user_id", getLoginUser().getId());
        formData.put("updater", getLoginUser().getId());
        formData.put("update_time", LocalDateTime.now());
        String tableName = (String) formData.get("tableName");
        formData.put("eventType", "update");
        formData.put("eventTime", "before");
        String bpmDefKey = (String) formData.get("bpm_def_key");
        LiteflowResponse liteflowResponse = flowsExecutor.execute2Resp(tableName, null, formData);
        if (!liteflowResponse.isSuccess()) {
            if (!(liteflowResponse.getCause() instanceof ChainNotFoundException)) {
                throw new ServiceException(500, liteflowResponse.getMessage());
            }
        }
        if (StringUtils.isNotEmpty(bpmDefKey)) {
            liteflowResponse = flowsExecutor.execute2Resp(bpmDefKey, null, formData);
            if (!liteflowResponse.isSuccess()) {
                if (!(liteflowResponse.getCause() instanceof ChainNotFoundException)) {
                    throw new ServiceException(500, liteflowResponse.getMessage());
                }
            }
        }
        JsonUtils.parseArrayToJSON(formData);
        DataSource master = DatasourceUtil.getDataSource("master");
        Entity entity = Entity.create(tableName);
        Entity where = Entity.create(tableName);
        //获取表里所有字段
        Table tableMeta = MetaUtil.getTableMeta(master, tableName);
        Collection<Column> columns = tableMeta.getColumns();
        Optional<Column> pk = columns.stream().filter(Column::isPk).findFirst();
        Map<String, Object> toSaveFormData = handleFormDataForSave(formData, tableName, columns);
        if (pk.isPresent()) {
            where.set(pk.get().getName(), formData.get(pk.get().getName()).toString());
        }
        entity.putAll(toSaveFormData);
        //审计字段
        putUpdateAuditEntityInsertData(entity, columns);
        try {
            Db.use().update(entity, where);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(500, "表单数据更新失败" + e.getMessage());
        }
        formData.put("eventType", "update");
        formData.put("eventTime", "after");
        liteflowResponse = flowsExecutor.execute2Resp(tableName, null, formData);
        if (!liteflowResponse.isSuccess()) {
            if (!(liteflowResponse.getCause() instanceof ChainNotFoundException)) {
                throw new ServiceException(500, liteflowResponse.getMessage());
            }
        }
        return success(true);
    }

    @Override
    public List<Entity> listByFk(String fk, String tableName) {
        DataSource master = DatasourceUtil.getDataSource("master");
        Entity where = Entity.create(tableName);
        where.set("fk", fk);
        try {
            List<Entity> entityList = Db.use().find(where);
            entityList.forEach(entity -> {
                String tp = entity.getStr("tp");
                if (StringUtils.isNotEmpty(tp)) {
                    String[] strings = tp.split(",");
                    List<String> list = Arrays.asList(strings);
                    entity.put("tp", list);
                }
            });
            return entityList;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(500, "表单数据查询失败" + e.getMessage());
        }
    }

    @Override
    public List<Entity> list(String tableName) {
        DataSource master = DatasourceUtil.getDataSource("master");
        Entity where = Entity.create(tableName);
        try {
            List<Entity> entityList = Db.use().find(where);
            entityList.forEach(entity -> {
                JsonUtils.parseJSONToArray(entity);
            });
            return entityList;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(500, "表单数据查询失败" + e.getMessage());
        }
    }

    @Override
    public CommonResult<String> createProcessInstanceApply(BpmProcessInstanceCreateCustomFormReqVO createReqVO) {
        Entity entity = getById(createReqVO.getYwid(), createReqVO.getTableName());
        //启动流程
        String processInstanceId = processInstanceService.createProcessInstance(getLoginUserId(),
                new BpmProcessInstanceCreateReqDTO()
                        .setProcessDefinitionKey(createReqVO.getBpmDefKey())
                        .setVariables(entity)
                        .setBusinessKey(String.valueOf(createReqVO.getYwid())));
        //完成制单节点

        return CommonResult.success(processInstanceId);
    }

    /**
     * 校验 Field，避免 field 重复
     *
     * @param fields field 数组
     */
    private void validateFields(List<String> fields) {
        if (true) { // TODO 芋艿：兼容 Vue3 工作流：因为采用了新的表单设计器，所以暂时不校验
            return;
        }
        Map<String, String> fieldMap = new HashMap<>(); // key 是 vModel，value 是 label
        for (String field : fields) {
            BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class);
            Assert.notNull(fieldDTO);
            String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel());
            // 如果不存在，则直接返回
            if (oldLabel == null) {
                continue;
            }
            // 如果存在，则报错
            throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel());
        }
    }

    /**
     * 处理保存的表单数据
     */
    @SneakyThrows
    private Map<String, Object> handleFormDataForSave(JSONObject formData, String tableName, Collection<Column> columns) {
        Map<String, Object> resultData = new HashMap<>(formData.size());
        Set<String> fieldNameList = formData.keySet();
        Collection<Column> tableMetaColumns = TableUtil.getColumns(tableName);
        for (String fieldName : fieldNameList) {

            for (Column tableMetaColumn : tableMetaColumns) {
                if (StrUtil.equalsIgnoreCase(fieldName, tableMetaColumn.getName())) {
                    tableMetaColumn.getTypeName();
                    String className = JdbcToJavaUtil.getClassName(tableMetaColumn);

                    if (!TIME_CLASS_NAME.contains(className)) {
                        Object val = formData.get(fieldName);
                        if (val != null && val instanceof String && StrUtil.isNotEmpty(val.toString())) {
                            String data = (String) val;
                            if (data.length() > tableMetaColumn.getSize()) {
                                throw new ServiceException(500, tableName + "表" + fieldName + "字段长度过长:" + data.length() + "，数据库是:" + tableMetaColumn.getSize());
                            }

                        }
                    }

                    //如果类型是时间类型  则需要特殊处理
                    if (TIME_CLASS_NAME.contains(className)) {
                        //时间类型  需要特殊处理
                        String dateStr = String.valueOf(formData.get(fieldName));
                        if (StrUtil.isNotEmpty(dateStr) && !"null".equalsIgnoreCase(dateStr) && !"undefined".equalsIgnoreCase(dateStr)) {
                            Date date = null;
                            try {
                                dateStr = dateStr.replace("/", "-");
                                date = DateUtil.parse(dateStr);
                                resultData.put(fieldName, date);
                            } catch (Exception e) {
                                resultData.put(fieldName, null);
                            }
                        } else {
                            resultData.put(fieldName, null);
                        }
                    } else {
                        if (EQ_CLASS_NAME.contains(className)) {
                            Object o = formData.get(fieldName);
                            if (o != null) {
                                if (StringUtils.isNotEmpty(o.toString())) {
                                    try {
                                        Double.parseDouble(o.toString());
                                    } catch (NumberFormatException e) {
                                        throw new ServiceException(500, tableName + "表" + fieldName + "应为数字类型，目前值是:" + formData.get(fieldName));
                                    }
                                }
                            }


                        }
                        resultData.put(fieldName, formData.get(fieldName));
                    }
                }
            }
        }
        return resultData;
    }

    /**
     * 填充审计字段 新增
     *
     * @param entity
     * @param columns
     */
    private void putAuditEntityInsertData(Entity entity, Collection<Column> columns) {
        for (Column column : columns) {
            if (StrUtil.equalsIgnoreCase(GlobalConstant.CREATE_USER_ID, column.getName())) {
                entity.set(GlobalConstant.CREATE_USER_ID, SecurityFrameworkUtils.getLoginUserId());
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.CREATE_DATE, column.getName())) {
                entity.set(GlobalConstant.CREATE_DATE, Timestamp.valueOf(LocalDateTime.now()));
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.DELETE_MARK, column.getName())) {
                entity.set(GlobalConstant.DELETE_MARK, 1);
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.ENABLED_MARK, column.getName())) {
                entity.set(GlobalConstant.ENABLED_MARK, 0);
            }

            if (StrUtil.equalsIgnoreCase(GlobalConstant.MODIFY_USER_ID, column.getName())) {
                entity.set(GlobalConstant.MODIFY_USER_ID, SecurityFrameworkUtils.getLoginUserId());
            }
            //兼容新版
            if (StrUtil.equalsIgnoreCase(GlobalConstant.CREATE_BY, column.getName())) {
                entity.set(GlobalConstant.CREATE_BY, SecurityFrameworkUtils.getLoginUserId());
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.UPDATE_BY, column.getName())) {
                entity.set(GlobalConstant.UPDATE_BY, SecurityFrameworkUtils.getLoginUserId());
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.UPDATE_DATE, column.getName())) {
                entity.set(GlobalConstant.UPDATE_DATE, Timestamp.valueOf(LocalDateTime.now()));
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.DEL_FLAG, column.getName())) {
                entity.set(GlobalConstant.DEL_FLAG, 1);
            }
        }
    }

    /**
     * 填充审计字段 新增
     *
     * @param entity
     * @param columns
     */
    private void putUpdateAuditEntityInsertData(Entity entity, Collection<Column> columns) {
        for (Column column : columns) {

            if (StrUtil.equalsIgnoreCase(GlobalConstant.MODIFY_USER_ID, column.getName())) {
                entity.set(GlobalConstant.MODIFY_USER_ID, SecurityFrameworkUtils.getLoginUserId());
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.UPDATE_BY, column.getName())) {
                entity.set(GlobalConstant.UPDATE_BY, SecurityFrameworkUtils.getLoginUserId());
            }
            if (StrUtil.equalsIgnoreCase(GlobalConstant.UPDATE_DATE, column.getName())) {
                entity.set(GlobalConstant.UPDATE_DATE, Timestamp.valueOf(LocalDateTime.now()));
            }
        }
    }

}
